home *** CD-ROM | disk | FTP | other *** search
/ Chip 2007 January, February, March & April / Chip-Cover-CD-2007-04d.zip / 04d / Hity z okladki / Plus Firma / AccessRT / Office1.cab / RPT2HTM4.XSL < prev    next >
Extensible Markup Language  |  2003-10-09  |  51KB  |  1,529 lines

  1. ∩╗┐<?xml version="1.0" encoding="UTF-8"?>
  2. <xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl">
  3.     <ox:meta inputtype="ReportML" outputtype="HTML4WebRpt" displayname="HTML 4.0 Sample (XSL)" xmlns:ox="urn:schemas-microsoft-com:officexsl"/>
  4.  
  5.     <xsl:template match="/">
  6.  
  7.         <xsl:pi name="xml">version="1.0"</xsl:pi>
  8.         
  9.         <xsl:element name="xsl:stylesheet">
  10.             <xsl:attribute name="xmlns:xsl">http://www.w3.org/TR/WD-xsl</xsl:attribute>
  11.             <xsl:attribute name="language">vbscript</xsl:attribute>
  12.  
  13.             <xsl:choose>
  14.                 <xsl:when expr="FInvalidReportML(this)">
  15.                     <xsl:element name="xsl:template">
  16.                         <xsl:attribute name="match">/</xsl:attribute>
  17.                         <xsl:eval no-entities="true">HandleErrors(this)</xsl:eval>
  18.                     </xsl:element>
  19.                 </xsl:when>
  20.                 <xsl:otherwise>
  21.                     <xsl:element name="xsl:template">
  22.                         <xsl:attribute name="match">/</xsl:attribute>
  23.  
  24.                         <HTML>
  25.                             <HEAD>
  26.                                 <META HTTP-EQUIV="Content-Type" CONTENT="text/html;charset=UTF-8"/>
  27.                                 <TITLE>
  28.                                     <xsl:value-of select="RPTML/REPORT[0]/TITLE"/>
  29.                                 </TITLE>
  30.                                 <STYLE TYPE="text/css">
  31.                                     <xsl:apply-templates select="RPTML/REPORT[0]/STYLE"/>
  32.                                 </STYLE>
  33.                             </HEAD>
  34.                                 <xsl:apply-templates select="RPTML/REPORT[0]"/>
  35.                         </HTML>
  36.  
  37.                     <xsl:element name="xsl:script">
  38.                         <xsl:cdata>
  39.                         'variable declaration
  40.                         dim cNodes
  41.                         dim iCurrNode
  42.                         dim rgNodes()
  43.                         dim objCurrNode
  44.                         dim sizeIncrement
  45.                         dim objGroupNodes
  46.                         dim rgFields()
  47.                         dim rgRowsources()
  48.                         dim rgGroupOn()
  49.                         dim rgGroupInterval()
  50.                         dim rgfGroupBoundaries()
  51.                         dim cGroups
  52.                         dim cGroupBoundaries
  53.                         dim rgGroupRowsources()
  54.  
  55.                         'variable initialization
  56.                         sizeIncrementGroup = 10
  57.                         sizeIncrementNode = 100
  58.                         cNodes = 0
  59.                         cGroups = 0
  60.                         cGroupBoundaries = 0
  61.                         iCurrNode = 0
  62.                         objCurrNode = null
  63.                         objGroupNodes = null
  64.                         ReDim rgNodes(sizeIncrement)
  65.                         ReDim rgFields(sizeIncrementGroup)
  66.                         ReDim rgRowsources(sizeIncrementGroup)
  67.                         ReDim rgGroupOn(sizeIncrementGroup)
  68.                         ReDim rgGroupInterval(sizeIncrementGroup)
  69.                         ReDim rgfGroupBoundaries(sizeIncrementNode)
  70.                         ReDim rgGroupRowsources(sizeIncrementGroup)
  71.                         
  72.  
  73.                         function SetGroupFilter(iLevel, strField, strRowsource, strGroupOn, strGroupInterval)
  74.                             if (cGroups Mod sizeIncrementGroup) = 0 then
  75.                                 ReDim Preserve rgFields(sizeIncrementGroup+cGroups)
  76.                                 ReDim Preserve rgRowsources(sizeIncrementGroup+cGroups)
  77.                                 ReDim Preserve rgGroupOn(sizeIncrementGroup+cGroups)
  78.                                 ReDim Preserve rgGroupInterval(sizeIncrementGroup+cGroups)
  79.                                 ReDim Preserve rgGroupRowsources(sizeIncrementGroup+cGroups)
  80.                             end if
  81.                             dim Field
  82.                             dim Rowsource
  83.                             dim GroupOn
  84.                             dim GroupInterval
  85.                             rgFields(cGroups) = strField
  86.                             rgRowsources(cGroups) = strRowsource
  87.                             rgGroupOn(cGroups) = strGroupOn
  88.                             rgGroupInterval(cGroups) = strGroupInterval
  89.                             cGroups = cGroups + 1
  90.                             SetGroupFilter = ""
  91.                         end function
  92.  
  93.                         function AppendNodeIndex(objNode)
  94.                             dim iNode
  95.                             dim fGroupBoundary
  96.                             iNode = childNumber(objNode) - 1
  97.                             if (cNodes Mod sizeIncrementNode) = 0 then
  98.                                 ReDim Preserve rgNodes(sizeIncrementNode+cNodes)
  99.                             end if
  100.                             rgNodes(cNodes) = iNode
  101.  
  102.                             if (cGroupBoundaries Mod sizeIncrementNode) = 0 then
  103.                                 ReDim Preserve rgfGroupBoundaries((sizeIncrementNode*cGroups)+cGroupBoundaries)
  104.                             end if
  105.  
  106.                             if (cGroups > 0) and (cNodes > 0) then
  107.                                 For i = 0 To (cGroups - 1)
  108.                                     set objCurr = objNode.selectSingleNode(rgFields(i))
  109.                                     set objPrev = rgGroupRowsources(i).item(rgNodes(cNodes-1)).selectSingleNode(rgFields(i))
  110.                                     rgfGroupBoundaries(cGroups*(cNodes-1) + i) = OnGroupBoundary(objPrev, objCurr, rgGroupOn(i), rgGroupInterval(i))
  111.                                 Next
  112.                             end if
  113.                             if (cGroups > 0) and (cNodes = 0) then
  114.                                 For i = 0 To (cGroups - 1)
  115.                                     set rgGroupRowsources(i) = objNode.selectNodes("/dataroot/" & rgRowsources(i))
  116.                                 Next
  117.                             end if
  118.  
  119.                             cGroupBoundaries = cGroupBoundaries + cGroups
  120.                             cNodes = cNodes + 1
  121.                             AppendNodeIndex = ""
  122.                         end function
  123.  
  124.                         function GetNodeIndex(iNode)            
  125.                             GetNodeIndex = rgNodes(iNode)
  126.                         end function
  127.                         
  128.                         function NextNode()                        
  129.                             iCurrNode = iCurrNode + 1
  130.                             NextNode = ""
  131.                         end function
  132.  
  133.                         function CacheCurrentNode(objNode)        
  134.                             set objCurrNode = objNode
  135.                             CacheCurrentNode = ""
  136.                         end function
  137.  
  138.                         function Page()
  139.                             Page = 1
  140.                         end function
  141.                         
  142.                         function Pages()
  143.                             Pages = 1
  144.                         end function
  145.  
  146.                         function ToString(varValue)
  147.                             if IsNull(varValue) then
  148.                                 ToString = ""
  149.                                 exit function
  150.                             end if
  151.                             ToString = "" & varValue
  152.                         end function
  153.  
  154.                         function Format(varValue, strFormat, strAdditionalArgs)
  155.  
  156.                             dim FormatTemp
  157.                             
  158.                             if (IsDate(varValue)) then
  159.                                 select case strFormat
  160.                                     case "General Date"
  161.                                         FormatTemp = FormatDateTime(varValue, vbGeneralDate)
  162.                                     case "Long Date"
  163.                                         FormatTemp = FormatDateTime(varValue, vbLongDate)
  164.                                     case "Medium Date"
  165.                                         FormatTemp = Day(varValue) & "-" & MonthName(Month(varValue), True) & "-" & Mid(Year(varValue), 3, 2)
  166.                                     case "Short Date"
  167.                                         FormatTemp = FormatDateTime(varValue, vbShortDate)
  168.                                     case "Long Time"
  169.                                         FormatTemp = FormatDateTime(varValue, vbLongTime)                            
  170.                                     case "Medium Time"
  171.                                         strTemp = FormatDateTime(varValue, vbLongTime)
  172.                                         if (IsNumeric(Mid(strTemp, 2, 1))) then
  173.                                             FormatTemp = Mid(strTemp,1,5) & Mid(strTemp, 9)
  174.                                         else
  175.                                             FormatTemp = Mid(strTemp,1,4) & Mid(strTemp, 9)
  176.                                         end if
  177.                                     case "Short Time"
  178.                                         FormatTemp = FormatDateTime(varValue, vbShortTime)
  179.                                     case else
  180.                                         FormatTemp = FormatDateTime(varValue, vbGeneralDate)
  181.                                 end select
  182.                             else 
  183.                                 if (IsNumeric(varValue)) then
  184.                                     select case strFormat
  185.                                         case "General Number"
  186.                                             FormatTemp = varValue
  187.                                         case "Currency"
  188.                                             Dim lcid
  189.                                             if (IsNumeric(strAdditionalArgs)) then
  190.                                                 lcid = SetLocale(strAdditionalArgs)
  191.                                                 FormatTemp = FormatCurrency(varValue)
  192.                                                 SetLocale(lcid)
  193.                                             end if
  194.  
  195.                                         case "Fixed"
  196.                                             if (IsNumeric(strAdditionalArgs)) then
  197.                                                 FormatTemp = FormatNumber(varValue, strAdditionalArgs, vbTrue, vbUseDefault, vbFalse)
  198.                                             else
  199.                                                 FormatTemp = FormatNumber(varValue, 2, vbTrue, vbUseDefault, vbFalse)
  200.                                             end if
  201.                                         case "Standard"
  202.                                             if (IsNumeric(strAdditionalArgs)) then
  203.                                                 FormatTemp = FormatNumber(varValue, strAdditionalArgs, vbUseDefault, vbUseDefault, vbTrue)
  204.                                             else
  205.                                                 FormatTemp = FormatNumber(varValue, 2, vbUseDefault, vbUseDefault, vbTrue)
  206.                                             end if
  207.                                         case "Percent"
  208.                                             if (IsNumeric(strAdditionalArgs)) then
  209.                                                 FormatTemp = FormatPercent(varValue, strAdditionalArgs)
  210.                                             else
  211.                                                 FormatTemp = FormatPercent(varValue)
  212.                                             end if
  213.                                         case "Scientific"
  214.                                             nExp = Int(Log(Abs(varValue))/Log(10))
  215.                                             nValue = Round(CDbl(varValue)/(10^CDbl(nExp)), 2)
  216.                                             if (Sgn(nExp) < 0) then
  217.                                                 FormatTemp = FormatNumber(nValue, 2, vbTrue, vbFalse, vbFalse) & "E-" & nExp
  218.                                             else
  219.                                                 FormatTemp = FormatNumber(nValue, 2, vbTrue, vbFalse, vbFalse) & "E+" & nExp
  220.                                             end if
  221.                                         case "True/False"
  222.                                             if (CBool(varValue)) then
  223.                                                 FormatTemp = "True"
  224.                                             else
  225.                                                 FormatTemp = "False"
  226.                                             end if
  227.                                         case "Yes/No"
  228.                                             if (CBool(varValue)) then
  229.                                                 FormatTemp = "Yes"
  230.                                             else
  231.                                                 FormatTemp = "No"
  232.                                             end if 
  233.                                         case "On/Off"
  234.                                             if (CBool(varValue)) then
  235.                                                 FormatTemp = "On"
  236.                                             else
  237.                                                 FormatTemp = "Off"
  238.                                             end if 
  239.                                     end select
  240.                                 end if
  241.                             end if
  242.  
  243.                             if IsEmpty(FormatTemp) then
  244.                                 FormatTemp = varValue
  245.                             end if
  246.  
  247.                             if FHasNoContent(FormatTemp) then
  248.                                 Format = "&nbsp;"
  249.                             else
  250.                                 Format = FormatTemp
  251.                             end if
  252.                                             
  253.                         end function
  254.  
  255.                         function FHasNoContent(objValue)
  256.                             if (IsNull(objValue) or IsEmpty(objValue) or objValue = "") then
  257.                                 FHasNoContent = true
  258.                             else
  259.                                 FHasNoContent = false
  260.                             end if
  261.                         end function
  262.  
  263.                         function IIf(fCond, objTrue, objFalse)
  264.                             if fCond then
  265.                                 IIf = objTrue
  266.                                 exit function
  267.                             end if
  268.                             IIf = objFalse
  269.                         end function
  270.  
  271.                         function nz(varValue, varReplace)
  272.                             if (varValue = null) or (varValue = "") then
  273.                                 nz = varReplace
  274.                             else
  275.                                 nz = varValue
  276.                             end if
  277.                         end function
  278.  
  279.                         function sum(strExpr, iLevel)
  280.                             Dim nSum
  281.  
  282.                             nSum = 0
  283.                             set objCurrNodeT = objCurrNode
  284.  
  285.                             For i = GetGroupLowerBound(iLevel) to GetGroupUpperBound(iLevel)
  286.                                 set objCurrNode = rgGroupRowsources(iLevel).item(rgNodes(i))
  287.                                 nSum = nSum + eval(strExpr)
  288.                             Next
  289.  
  290.                             set objCurrNode = objCurrNodeT
  291.                             sum = nSum
  292.                         end function
  293.  
  294.                         function Count(strExpr, iLevel)
  295.                             Count = GetGroupUpperBound(iLevel) - GetGroupLowerBound(iLevel) + 1
  296.                         end function
  297.  
  298.                         function Avg(strExpr, iLevel)
  299.                             Dim nSum
  300.                             Dim nCount
  301.                             nSum = Sum(strExpr, iLevel)
  302.                             nCount = Count(strExpr, iLevel)
  303.                             if nCount > 0 then
  304.                                 Avg = nSum / nCount
  305.                             else
  306.                                 Avg = nSum
  307.                             end if
  308.                         end function
  309.  
  310.                         function min(strExpr, strArgs)
  311.                             Dim nMin
  312.  
  313.                             nMin = Eval(strExpr)
  314.                             set objCurrNodeT = objCurrNode
  315.                             
  316.                             For i = GetGroupLowerBound(iLevel) to GetGroupUpperBound(iLevel)
  317.                                 set objCurrNode = rgGroupRowsources(iLevel).item(rgNodes(i))
  318.                                 nTemp = eval(strExpr)
  319.                                 if (nTemp < nMin) then
  320.                                     nMin = nTemp
  321.                                 end if
  322.                             Next
  323.  
  324.                             set objCurrNode = objCurrNodeT
  325.                             min = nMin
  326.                         end function    
  327.  
  328.                         function max(strExpr, strArgs)
  329.                             Dim nMax
  330.  
  331.                             nMax = Eval(strExpr)
  332.                             set objCurrNodeT = objCurrNode
  333.                             
  334.                             For i = GetGroupLowerBound(iLevel) to GetGroupUpperBound(iLevel)
  335.                                 set objCurrNode = rgGroupRowsources(iLevel).item(rgNodes(i))
  336.                                 nTemp = eval(strExpr)
  337.                                 if (nTemp > nMax) then
  338.                                     nMax = nTemp
  339.                                 end if
  340.                             Next
  341.  
  342.                             set objCurrNode = objCurrNodeT
  343.                             max = nMax
  344.                         end function                    
  345.  
  346.                         function GetValue(strRef, nType)
  347.                             set objNode = objCurrNode.selectSingleNode(strRef)
  348.                             if (objNode is nothing) or IsNull(objNode) or IsEmpty(objNode) or not(IsObject(objNode)) then
  349.                                 GetValue = ""
  350.                                 exit function
  351.                             end if
  352.  
  353.                             select case nType
  354.                                 case 2         ' adSmallInt
  355.                                     GetValue = CLng(objNode.text)
  356.                                     exit function
  357.                                 case 3         ' adInteger
  358.                                     GetValue = CLng(objNode.text)
  359.                                     exit function
  360.                                 case 20        ' adBigInt
  361.                                     GetValue = CLng(objNode.text)
  362.                                     exit function
  363.                                 case 17        ' adUnsignedTinyInt
  364.                                     GetValue = CLng(objNode.text)
  365.                                     exit function
  366.                                 case 18        ' adUnsignedSmallInt
  367.                                     GetValue = CLng(objNode.text)
  368.                                     exit function
  369.                                 case 19        ' adUnsignedInt
  370.                                     GetValue = CLng(objNode.text)
  371.                                     exit function
  372.                                 case 21        ' adUnsignedBigInt
  373.                                     GetValue = CLng(objNode.text)
  374.                                     exit function
  375.                                 case 4        ' adSingle
  376.                                     GetValue = CDbl(objNode.text)
  377.                                     exit function
  378.                                 case 5        ' adDouble
  379.                                     GetValue = CDbl(objNode.text)
  380.                                     exit function
  381.                                 case 6        ' adCurrency
  382.                                     GetValue = CCur(objNode.text)
  383.                                     exit function
  384.                                 case 14        ' adDecimal
  385.                                     GetValue = CDbl(objNode.text)
  386.                                     exit function
  387.                                 case 131    ' adNumeric
  388.                                     GetValue = CDbl(objNode.text)
  389.                                     exit function
  390.                                 case 139    ' adVarNumeric
  391.                                     GetValue = CDbl(objNode.text)
  392.                                     exit function
  393.                                 case 11        ' adBoolean
  394.                                     GetValue = CBool(objNode.text)
  395.                                     exit function
  396.                                 case 7         ' adDate
  397.                                     GetValue = BuildDateFromStr(objNode.text, true)
  398.                                     exit function
  399.                                 case 133    ' adDBDate
  400.                                     GetValue = BuildDateFromStr(objNode.text, true)
  401.                                     exit function
  402.                                 case 134    ' adDBTime
  403.                                     GetValue = BuildDateFromStr(objNode.text, true)
  404.                                     exit function
  405.                                 case 135    ' adDBTimeStamp
  406.                                     GetValue = BuildDateFromStr(objNode.text, true)
  407.                                     exit function
  408.                                 case 8        ' adBSTR
  409.                                 case 120    ' adChar
  410.                                 case 200    ' adVarChar
  411.                                 case 201    ' adLongVarChar
  412.                                 case 130    ' adWChar:
  413.                                 case 202    ' adVarWChar
  414.                                 case 203    ' adLongVarWChar    
  415.                             end select
  416.             
  417.                             'Default are strings
  418.                             GetValue = objNode.text
  419.                         end function
  420.  
  421.                         function GetGroupLowerBound(iLevel)
  422.                             iBound = iCurrNode-1
  423.                             iIndex = cGroups*(iBound) + iLevel
  424.  
  425.                             'Error checking
  426.                             if (iIndex >= cGroupBoundaries or iBound >= cNodes) then
  427.                                 GetGroupLowerBound = 0
  428.                                 exit function
  429.                             end if
  430.                                                 
  431.                             do while (iIndex >= 0 and iBound >= 1)
  432.                                 if rgfGroupBoundaries(iIndex) then
  433.                                     GetGroupLowerBound = iBound + 1
  434.                                     exit function
  435.                                 else
  436.                                     iBound = iBound - 1
  437.                                     iIndex = cGroups*(iBound) + iLevel
  438.                                 end if
  439.                             loop
  440.  
  441.                             GetGroupLowerBound = 0
  442.                         end function
  443.  
  444.                         function GetGroupUpperBound(iLevel)
  445.                             iBound = iCurrNode
  446.                             iIndex = cGroups*(iBound) + iLevel
  447.  
  448.                             'Error checking
  449.                             if (iIndex < 0 or iBound < 0) then
  450.                                 GetGroupUpperBound = cNodes - 1
  451.                                 exit function
  452.                             end if
  453.                                                 
  454.                             do while (iIndex < cGroupBoundaries and iBound < (cNodes - 1))
  455.                                 if rgfGroupBoundaries(iIndex) then
  456.                                     GetGroupUpperBound = iBound
  457.                                     exit function
  458.                                 else
  459.                                     iBound = iBound + 1
  460.                                     iIndex = cGroups*(iBound) + iLevel
  461.                                 end if
  462.                             loop
  463.  
  464.                             GetGroupUpperBound = cNodes - 1
  465.                         end function
  466.  
  467.                         function OnGroupHeader(objElem, iLevel)
  468.                             if onFirstNode then
  469.                                 OnGroupHeader = true
  470.                                 exit function
  471.                             end if
  472.  
  473.                             iIndex = cGroups*(iCurrNode-1) + iLevel
  474.                             if (iLevel >= cGroups) or (iIndex >= cGroupBoundaries) then
  475.                                 OnGroupHeader = false
  476.                                 exit function
  477.                             end if
  478.  
  479.                             OnGroupHeader = rgfGroupBoundaries(iIndex)
  480.                         end function
  481.  
  482.                         function OnGroupFooter(objElem, iLevel)
  483.                             if OnLastNode then
  484.                                 OnGroupFooter = true
  485.                                 exit function
  486.                             end if
  487.  
  488.                             iIndex = cGroups*(iCurrNode) + iLevel
  489.                             if (iLevel >= cGroups) or (iIndex >= cGroupBoundaries) then
  490.                                 OnGroupFooter = false
  491.                                 exit function
  492.                             end if
  493.  
  494.                             OnGroupFooter = rgfGroupBoundaries(iIndex)
  495.                         end function
  496.  
  497.                         function OnGroupBoundary(objPrev, objCurr, strGroupOn, strInterval)
  498.                             dim strPrevValue
  499.                             dim strCurrValue
  500.  
  501.                             if (objPrev is nothing) and (objCurr is nothing) then
  502.                                 OnGroupBoundary = false
  503.                                 exit function
  504.                             else 
  505.                                 if (objPrev is nothing) or (objCurr is nothing) then
  506.                                     OnGroupBoundary = true
  507.                                     exit function
  508.                                 end if 
  509.                             end if
  510.  
  511.                             strPrevValue = objPrev.text
  512.                             strCurrValue = objCurr.text
  513.                         
  514.                             select case strGroupOn
  515.                                 case "each value"
  516.                                     if (strPrevValue = strCurrValue) then
  517.                                         OnGroupBoundary = false
  518.                                         exit function
  519.                                     end if
  520.                                 case "prefix characters"
  521.                                     dim cChars 
  522.                                     cChars = cInt(strInterval)
  523.                                     if (Mid(strPrevValue, 1, cChars) = Mid(strCurrValue, 1, cChars)) then
  524.                                         OnGroupBoundary = false
  525.                                         exit function
  526.                                     end if                        
  527.                                 case "year"
  528.                                     if (Mid(strPrevValue, 1, 4) = Mid(strCurrValue, 1, 4)) then
  529.                                         OnGroupBoundary = false
  530.                                         exit function
  531.                                     end if
  532.                                 case "quarter"
  533.                                     if (Mid(strPrevValue, 1, 4) = Mid(strCurrValue, 1, 4)) then
  534.                                         dim qtrPrev
  535.                                         dim qtrCurr
  536.                                         qtrPrev = Int(Cint(Mid(strPrevValue, 6, 2) - 1) / 3)
  537.                                         qtrCurr = Int(Cint(Mid(strCurrValue, 6, 2) - 1) / 3)
  538.                                         if (qtrPrev = qtrCurr) then
  539.                                             OnGroupBoundary = false
  540.                                             exit function
  541.                                         end if
  542.                                     end if
  543.                                 case "month"
  544.                                     if (Mid(strPrevValue, 1, 7) = Mid(strCurrValue, 1, 7)) then
  545.                                         OnGroupBoundary = false
  546.                                         exit function
  547.                                     end if
  548.                                 case "week"
  549.                                     if (Mid(strPrevValue, 1, 4) = Mid(strCurrValue, 1, 4)) then
  550.                                         dim datePrev
  551.                                         dim dateCurr
  552.                                         datePrev = BuildDateFromStr(strPrevValue, false)
  553.                                         dateCurr = BuildDateFromStr(strCurrValue, false)
  554.                                         if (DatePart("ww", datePrev) = DatePart("ww", dateCurr)) then
  555.                                             OnGroupBoundary = false
  556.                                             exit function
  557.                                         end if
  558.                                     end if
  559.                                 case "day"
  560.                                     if (Mid(strPrevValue, 1, 10) = Mid(strCurrValue, 1, 10)) then
  561.                                         OnGroupBoundary = false
  562.                                         exit function
  563.                                     end if
  564.                                 case "hour"
  565.                                     if (Mid(strPrevValue, 1, 13) = Mid(strCurrValue, 1, 13)) then
  566.                                         OnGroupBoundary = false
  567.                                         exit function
  568.                                     end if
  569.                                 case "minute"
  570.                                     if (Mid(strPrevValue, 1, 16) = Mid(strCurrValue, 1, 16)) then
  571.                                         OnGroupBoundary = false
  572.                                         exit function
  573.                                     end if
  574.                                 case "interval"
  575.                                     if (Int(CInt(strPrevValue) / CInt(strInterval)) = Int(CInt(strCurrValue) / CInt(strInterval))) then
  576.                                         OnGroupBoundary = false
  577.                                         exit function
  578.                                     end if
  579.                             end select
  580.                         
  581.                             OnGroupBoundary = true
  582.                         end function
  583.  
  584.                         function OnFirstNode
  585.                             if iCurrNode = 0 then
  586.                                 OnFirstNode = true
  587.                                 exit function
  588.                             end if
  589.                             OnFirstNode = false
  590.                         end function
  591.  
  592.                         function OnLastNode
  593.                             if iCurrNode = (cNodes-1) then
  594.                                 OnLastNode = true
  595.                                 exit function
  596.                             end if
  597.                             OnLastNode = false
  598.                         end function
  599.  
  600.                         function BuildDateFromStr(strDate, fIncludeTime)
  601.                             Dim Date
  602.                             Date = DateSerial(Mid(strDate, 1, 4), Mid(strDate, 6, 2), Mid(strDate, 9, 2))
  603.  
  604.                             if (fIncludeTime) then
  605.                                 Date = DateAdd("h", Mid(strDate, 12, 2), Date)
  606.                                 Date = DateAdd("n", Mid(strDate, 15, 2), Date)
  607.                                 Date = DateAdd("s", Mid(strDate, 18, 2), Date)
  608.                             end if
  609.                             
  610.                             BuildDateFromStr = Date
  611.                         end function
  612.                         </xsl:cdata>
  613.                     </xsl:element>
  614.                     </xsl:element>
  615.                 </xsl:otherwise>
  616.             </xsl:choose>
  617.         </xsl:element>
  618.  
  619.     </xsl:template>
  620.  
  621.     <xsl:template match="STYLE">
  622.         .<xsl:value-of select="@id"/> { <xsl:eval>GetTextStyle(this, false)</xsl:eval> }
  623.     </xsl:template>
  624.  
  625.     <xsl:template match="REPORT">
  626.         <xsl:element name="BODY">
  627.             <xsl:attribute name="link"><xsl:eval>GetNodeText(this, "LINK", "#0000ff")</xsl:eval></xsl:attribute>
  628.             <xsl:attribute name="vlink"><xsl:eval>GetNodeText(this, "VLINK", "#800080")</xsl:eval></xsl:attribute>
  629.             <xsl:if test="BACKGROUND-IMAGE">
  630.                 <xsl:attribute name="style"><xsl:eval>GetBodyStyle(this.selectSingleNode("/RPTML/REPORT[0]/BACKGROUND-IMAGE"))</xsl:eval></xsl:attribute>
  631.             </xsl:if>
  632.  
  633.         <xsl:eval no-entities="true">GetTableTags(this, true)</xsl:eval>
  634.  
  635.         <xsl:eval no-entities="true">GetGroupFilters(this.selectSingleNode("/RPTML"))</xsl:eval>
  636.  
  637.         <xsl:choose>
  638.             <xsl:when test="ENCODED-RECORD-SOURCE">
  639.                 
  640.                 <xsl:element name="xsl:for-each">
  641.                     <xsl:attribute name="select">/dataroot/<xsl:value-of select="ENCODED-RECORD-SOURCE"/></xsl:attribute>
  642.                     <xsl:if test="GROUP-LEVEL">
  643.                         <xsl:attribute name="order-by"><xsl:eval>GetOrderByClause(this)</xsl:eval></xsl:attribute>
  644.                     </xsl:if>
  645.                     <xsl:element name="xsl:eval">AppendNodeIndex(me)</xsl:element>
  646.                 </xsl:element>
  647.                 
  648.  
  649.                 <xsl:element name="xsl:for-each">
  650.  
  651.                     <xsl:attribute name="select">/dataroot/<xsl:value-of select="ENCODED-RECORD-SOURCE"/></xsl:attribute>
  652.                     <xsl:if test="GROUP-LEVEL">
  653.                         <xsl:attribute name="order-by"><xsl:eval>GetOrderByClause(this)</xsl:eval></xsl:attribute>
  654.                     </xsl:if>
  655.  
  656.                     <xsl:element name="xsl:eval">CacheCurrentNode(me)</xsl:element>
  657.  
  658.                     <xsl:element name="xsl:if">
  659.                         <xsl:attribute name="expr">OnFirstNode</xsl:attribute>
  660.                         <xsl:apply-templates select="SECTION[@type='report_header']"/>
  661.                         <xsl:apply-templates select="SECTION[@type='page_header']"/>
  662.                     </xsl:element>
  663.  
  664.                     <!--number() is child number, Dependent on old namespace, xmlns:xsl="http://www.w3.org/TR/WD-xsl"-->
  665.                     <xsl:for-each select="GROUP-LEVEL[GROUP-HEADER='true']" order-by="+ number(@id)">
  666.                         <xsl:element name="xsl:if">
  667.                             <xsl:attribute name="expr"><xsl:eval>GetHeaderCondition(this)</xsl:eval></xsl:attribute>
  668.                             <xsl:apply-templates select="../SECTION[@type='group_header' and @idref=context()/@id]"/>
  669.                         </xsl:element>
  670.                     </xsl:for-each>
  671.  
  672.                     <xsl:if test="SECTION[@type='detail']/REPORT-ITEM">
  673.                         <xsl:apply-templates select="SECTION[@type='detail']"/>
  674.                     </xsl:if>
  675.  
  676.                     <!--number() is child number, Dependent on old namespace, xmlns:xsl="http://www.w3.org/TR/WD-xsl"-->
  677.                     <xsl:for-each select="GROUP-LEVEL[GROUP-FOOTER='true']" order-by="- number(@id)">
  678.                     <xsl:element name="xsl:if">
  679.                             <xsl:attribute name="expr"><xsl:eval>GetFooterCondition(this)</xsl:eval></xsl:attribute>
  680.                             <xsl:apply-templates select="../SECTION[@type='group_footer' and @idref=context()/@id]"/>
  681.                         </xsl:element>
  682.                     </xsl:for-each>
  683.  
  684.                     <xsl:element name="xsl:if">
  685.                         <xsl:attribute name="expr">OnLastNode</xsl:attribute>
  686.                         <xsl:apply-templates select="SECTION[@type='page_footer']"/>
  687.                         <xsl:apply-templates select="SECTION[@type='report_footer']"/>
  688.                     </xsl:element>
  689.  
  690.                     
  691.                     <xsl:element name="xsl:eval">NextNode()</xsl:element>
  692.                     
  693.                 </xsl:element>
  694.             </xsl:when>
  695.             <xsl:otherwise>
  696.                 <xsl:apply-templates select="SECTION[@type='report_header']"/>
  697.                 <xsl:apply-templates select="SECTION[@type='page_header']"/>
  698.                 <xsl:apply-templates select="SECTION[@type='page_footer']"/>
  699.                 <xsl:apply-templates select="SECTION[@type='report_footer']"/>
  700.             </xsl:otherwise>
  701.         </xsl:choose>
  702.         <xsl:eval no-entities="true">GetTableTags(this, false)</xsl:eval>
  703.         </xsl:element>
  704.     </xsl:template>
  705.  
  706.     <xsl:template match="SECTION[../LAYOUT='absolute']">
  707.         <xsl:element name="DIV">
  708.             <xsl:attribute name="style"><xsl:eval>GetTextStyle(this, true)</xsl:eval>; POSITION: relative</xsl:attribute>
  709.             <xsl:apply-templates select="REPORT-TEXT|REPORT-ITEM"/>
  710.         </xsl:element>
  711.     </xsl:template>
  712.  
  713.     <xsl:template match="SECTION[../LAYOUT='grid']">
  714.         <TR><xsl:apply-templates select="REPORT-TEXT|REPORT-ITEM"/></TR>
  715.     </xsl:template>
  716.  
  717.     <xsl:template match="REPORT-TEXT">
  718.         <xsl:eval no-entities="true">this.text</xsl:eval>
  719.     </xsl:template>
  720.  
  721.     <xsl:template match="REPORT-ITEM[(@type='text-box' or @type='label' or @type='combo-box' or @type='list-box') and not(HREF)]">
  722.         <xsl:choose>
  723.             <xsl:when test="../..[LAYOUT='absolute']">
  724.                 <xsl:element name="SPAN">
  725.                     <xsl:if test="CLASS"><xsl:attribute name="class"><xsl:value-of select="CLASS"/></xsl:attribute></xsl:if>
  726.                     <xsl:attribute name="style"><xsl:eval>GetTextStyle(this, false)</xsl:eval>; OVERFLOW: hidden; POSITION: absolute</xsl:attribute>
  727.                     <xsl:apply-templates />
  728.                 </xsl:element>
  729.             </xsl:when>
  730.  
  731.             <xsl:when test="../..[LAYOUT='grid']">
  732.                 <xsl:if test="..[@type='page_header']">
  733.                     <TH>
  734.                     <xsl:attribute name="style">width: <xsl:value-of select="COLUMN-WIDTH"/></xsl:attribute>
  735.                     <xsl:if test="CLASS"><xsl:attribute name="class"><xsl:value-of select="CLASS"/></xsl:attribute></xsl:if>
  736.                     <xsl:apply-templates />
  737.                     </TH>
  738.                 </xsl:if>
  739.  
  740.                 <xsl:if test="..[@type='detail']">
  741.                     <TD>
  742.                     <xsl:if test="CLASS"><xsl:attribute name="class"><xsl:value-of select="CLASS"/></xsl:attribute></xsl:if>
  743.                     <xsl:apply-templates />
  744.                     </TD>
  745.                 </xsl:if>
  746.             </xsl:when>
  747.         </xsl:choose>
  748.     </xsl:template>
  749.  
  750.     <xsl:template match="REPORT-ITEM[(@type='text-box' or @type='label') and HREF]">
  751.         <xsl:element name="A">
  752.             <xsl:attribute name="HREF"><xsl:value-of select="HREF"/></xsl:attribute>
  753.             <xsl:choose>
  754.                 <xsl:when test="../..[LAYOUT='absolute']">
  755.                     <xsl:element name="SPAN">
  756.                         <xsl:if test="CLASS"><xsl:attribute name="class"><xsl:value-of select="CLASS"/></xsl:attribute></xsl:if>
  757.                         <xsl:attribute name="style"><xsl:eval>GetHyperlinkStyle(this)</xsl:eval>; OVERFLOW: hidden; POSITION: absolute</xsl:attribute>
  758.                         <xsl:apply-templates />
  759.                     </xsl:element>
  760.                 </xsl:when>
  761.  
  762.                 <xsl:when test="../..[LAYOUT='grid']">
  763.                     <xsl:if test="..[@type='page_header']">
  764.                         <xsl:attribute name="style">width: <xsl:value-of select="COLUMN-WIDTH"/></xsl:attribute>
  765.                         <xsl:if test="CLASS"><xsl:attribute name="class"><xsl:value-of select="CLASS"/></xsl:attribute></xsl:if>
  766.                         <xsl:apply-templates />
  767.                     </xsl:if>
  768.  
  769.                     <xsl:if test="..[@type='detail']">
  770.                         <xsl:if test="CLASS"><xsl:attribute name="class"><xsl:value-of select="CLASS"/></xsl:attribute></xsl:if>
  771.                         <xsl:apply-templates />
  772.                     </xsl:if>
  773.                 </xsl:when>
  774.             </xsl:choose>
  775.         </xsl:element>
  776.     </xsl:template>
  777.  
  778.     <xsl:template match="REPORT-ITEM[@type='check-box']">
  779.         <xsl:element name="xsl:choose">
  780.             <xsl:element name="xsl:when">
  781.                 <xsl:attribute name="expr"><xsl:eval>GetCheckboxCondition(this)</xsl:eval> = 0</xsl:attribute>
  782.                 <xsl:element name="INPUT">
  783.                     <xsl:attribute name="TYPE">checkbox</xsl:attribute>
  784.                     <xsl:attribute name="style"><xsl:eval>GetCheckboxStyle(this)</xsl:eval>; POSITION: absolute</xsl:attribute>
  785.                 </xsl:element>
  786.             </xsl:element>
  787.             <xsl:element name="xsl:otherwise">
  788.                 <xsl:element name="INPUT">
  789.                     <xsl:attribute name="TYPE">checkbox</xsl:attribute>
  790.                     <xsl:attribute name="CHECKED"/>
  791.                     <xsl:attribute name="style"><xsl:eval>GetCheckboxStyle(this)</xsl:eval>; POSITION: absolute</xsl:attribute>
  792.                 </xsl:element>
  793.             </xsl:element>
  794.         </xsl:element>
  795.     </xsl:template>
  796.  
  797.     <xsl:template match="REPORT-ITEM[@type='line']">
  798.         <xsl:element name="HR">
  799.             <xsl:if test="@id"><xsl:attribute name="id"><xsl:value-of select="@id"/></xsl:attribute></xsl:if>
  800.             <xsl:if test="CLASS"><xsl:attribute name="class"><xsl:value-of select="CLASS"/></xsl:attribute></xsl:if>
  801.             <xsl:attribute name="style"><xsl:eval>GetLineStyle(this)</xsl:eval>; POSITION: absolute</xsl:attribute>
  802.         </xsl:element>
  803.     </xsl:template>
  804.  
  805.     <xsl:template match="REPORT-ITEM[@type='rectangle']">
  806.         <xsl:element name="DIV">
  807.             <xsl:if test="@id"><xsl:attribute name="id"><xsl:value-of select="@id"/></xsl:attribute></xsl:if>
  808.             <xsl:if test="CLASS"><xsl:attribute name="class"><xsl:value-of select="CLASS"/></xsl:attribute></xsl:if>
  809.             <xsl:attribute name="style"><xsl:eval>GetRectangleStyle(this)</xsl:eval>; POSITION: absolute</xsl:attribute>
  810.             <xsl:eval no-entities="true">'<xsl:eval no-entities="true">"&amp;nbsp;"</xsl:eval>'</xsl:eval>
  811.         </xsl:element>
  812.     </xsl:template>
  813.  
  814.     <xsl:template match="REPORT-ITEM[@type='image']">
  815.         <xsl:element name="IMG">
  816.             <xsl:if test="@id"><xsl:attribute name="id"><xsl:value-of select="@id"/></xsl:attribute></xsl:if>
  817.             <xsl:if test="CLASS"><xsl:attribute name="class"><xsl:value-of select="CLASS"/></xsl:attribute></xsl:if>
  818.             <xsl:attribute name="style"><xsl:eval>GetImageStyle(this)</xsl:eval>; POSITION: absolute</xsl:attribute>
  819.             <xsl:if test="SRC"><xsl:attribute name="SRC"><xsl:value-of select="SRC"/></xsl:attribute></xsl:if>
  820.         </xsl:element>
  821.     </xsl:template>
  822.  
  823.     <xsl:template match="ENCODED-CONTROL-SOURCE[@type='expression']">
  824.         <xsl:element name="xsl:eval"><xsl:attribute name="no-entities">true</xsl:attribute><xsl:eval no-entities="true">FixExpression(this, this.selectSingleNode("../FORMAT"), this.selectSingleNode("../DECIMAL-PLACES"), this.selectSingleNode("../FORMAT-LCID"), true)</xsl:eval></xsl:element>
  825.     </xsl:template>
  826.  
  827.     <xsl:template match="ENCODED-CONTROL-SOURCE[not(@type) and text()!='']">
  828.         <xsl:choose>
  829.             <xsl:when test="/RPTML/REPORT/DATA-MODEL/ROW-SOURCE[@id=/RPTML/REPORT[0]/ENCODED-RECORD-SOURCE]/FIELD[@id=context()!text() and (@datatype='128' or @datatype='204' or @datatype='205')]">
  830.                 <xsl:element name="xsl:if">
  831.                     <xsl:attribute name="test"><xsl:eval>this.text</xsl:eval></xsl:attribute>
  832.                     [...]
  833.                 </xsl:element>
  834.             </xsl:when>
  835.             <xsl:otherwise>
  836.                 <xsl:element name="xsl:eval"><xsl:attribute name="no-entities">true</xsl:attribute><xsl:eval>AddFormatting("GetValue(\"" + FixupFieldNames(this.text, this, true) + "\", " + GetDataType(this.selectSingleNode("../CONTROL-SOURCE")) + ")", this.selectSingleNode("../FORMAT"), this.selectSingleNode("../DECIMAL-PLACES"), this.selectSingleNode("../FORMAT-LCID"))</xsl:eval></xsl:element>
  837.             </xsl:otherwise>
  838.         </xsl:choose>
  839.     </xsl:template>
  840.  
  841.     <xsl:template match="CAPTION[not(../ENCODED-CONTROL-SOURCE)]">
  842.         <xsl:value-of select="."/>
  843.     </xsl:template>
  844.  
  845.     <xsl:script><![CDATA[
  846.  
  847.         function GetOrderByClause(objElem)
  848.             {
  849.             var strOrderBy = "";
  850.  
  851.             var objNodes = objElem.selectNodes("GROUP-LEVEL");
  852.             for (var iNode = 0; iNode < objNodes.length; iNode++)
  853.                 {
  854.                 if (iNode > 0)
  855.                     strOrderBy = strOrderBy + ";";
  856.                 var objNode = objNodes.item(iNode);
  857.                 if (objNode.selectSingleNode("SORT-ORDER") == null || objNode.selectSingleNode("SORT-ORDER").text == "ascending")
  858.                     strOrderBy = strOrderBy + "+ ";
  859.                 else
  860.                     strOrderBy = strOrderBy + "- ";
  861.  
  862.                 var strDataType = GetNodeText(objElem, "/RPTML/REPORT[0]/DATA-MODEL/ROW-SOURCE/FIELD[@id = '" + objNode.selectSingleNode("CONTROL-SOURCE").text + "']/@datatype", "8");
  863.                 if (IsNumberType(Number(strDataType), false))
  864.                     strOrderBy = strOrderBy + "number(" + objNode.selectSingleNode("ENCODED-CONTROL-SOURCE").text + ")";
  865.                 else
  866.                     strOrderBy = strOrderBy + objNode.selectSingleNode("ENCODED-CONTROL-SOURCE").text;
  867.                 }
  868.  
  869.             return strOrderBy;
  870.             }
  871.  
  872.         function GetGroupFilters(objElem)
  873.             {
  874.             var objLevels = objElem.selectNodes("/RPTML/REPORT[0]/GROUP-LEVEL");
  875.             var strFilter = "";
  876.             var strField = "";
  877.             var strRowsource = "";
  878.             var strGroupOn = "";
  879.             var strGroupInterval = "";
  880.             var objLevel;
  881.  
  882.             for (var iLevel = 0; iLevel < objLevels.length; iLevel++)
  883.                 {
  884.                 objLevel = objLevels.item(iLevel);
  885.                 if (objLevel != null)
  886.                     {
  887.                     strField = GetNodeText(objLevel, "ENCODED-CONTROL-SOURCE", "");
  888.                     strRowsource = GetNodeText(objLevel, "../ENCODED-RECORD-SOURCE", "");
  889.                     strGroupOn = GetNodeText(objLevel, "GROUP-ON", "each value");
  890.                     strGroupInterval = GetNodeText(objLevel, "GROUP-INTERVAL", "1");
  891.                     strFilter += "<xsl:eval>SetGroupFilter(" + 
  892.                                   iLevel + ", \"" + 
  893.                                   strField + "\", \"" + 
  894.                                   strRowsource + "\", \"" + 
  895.                                   strGroupOn + "\", \"" + 
  896.                                   strGroupInterval + "\")</xsl:eval>"; 
  897.                     }
  898.                 }
  899.             return strFilter;
  900.             }
  901.  
  902.         function GetHeaderCondition(objLevel)
  903.             {
  904.             var iLevel = childNumber(objLevel) - 1;
  905.             return "OnGroupHeader(me, " + iLevel + ")";
  906.             }
  907.  
  908.         function GetFooterCondition(objLevel)
  909.             {
  910.             var iLevel = childNumber(objLevel) - 1;
  911.             return "OnGroupFooter(me, " + iLevel + ")";
  912.             }
  913.  
  914.         var rgFunc  = new Array("IIf");
  915.         var rgTotal = new Array("Sum", "Count", "Avg", "Min", "Max");
  916.  
  917.         function FixExpression(objElem, objFormat, objDecimalPlaces, objLcid, fUseFormat)
  918.             {
  919.             var objNodes;
  920.             var strExpr = objElem.text;
  921.  
  922.             strExpr = FixInnerQuotes(strExpr);
  923.  
  924.             for (var iFunc = 0; iFunc < rgFunc.length; iFunc++) //undone check if we really need this, case sensitive
  925.                 strExpr = strExpr.replace(new RegExp(rgFunc[iFunc] + "\\(", "gi"), rgFunc[iFunc] + "(");
  926.             
  927.             strExpr = strExpr.replace(/&/g, '&');
  928.  
  929.             strExpr = strExpr.replace(/\[page\]/gi, 'Page()');
  930.             strExpr = strExpr.replace(/\[pages\]/gi, 'Pages()');
  931.  
  932.             strExpr = strExpr.replace(/is\s+null/gi, '= \"\"');
  933.             strExpr = strExpr.replace(/is\s+not\s+null/gi, '<> \"\"');
  934.  
  935.             strExpr = FixNzFunction(strExpr);
  936.  
  937.             //
  938.             // Replace references to other expressions with expression contents
  939.             //
  940.  
  941.             if (objElem.selectSingleNode("@id") != null)
  942.                 objNodes = objElem.selectNodes("/RPTML/REPORT[0]/SECTION/REPORT-ITEM[@id != '' and @id != '" + objElem.selectSingleNode("@id").text + "']");
  943.             else
  944.                 objNodes = objElem.selectNodes("/RPTML/REPORT[0]/SECTION/REPORT-ITEM[@id != '']");
  945.  
  946.             for (var iNode = 0; iNode < objNodes.length; iNode++)
  947.                 {
  948.                 var objNode = objNodes.item(iNode);
  949.                 if (objNode.selectSingleNode("ENCODED-CONTROL-SOURCE[@type='expression']") != null)
  950.                     {
  951.                     var strNodeID = objNode.selectSingleNode("@id").text;
  952.                     var strSource = objNode.selectSingleNode("ENCODED-CONTROL-SOURCE").text;
  953.                     strExpr = strExpr.replace(new RegExp("\\[" + strNodeID + "\\]", "gi"), "(" + strSource + ")");
  954.                     }
  955.                 }
  956.  
  957.             if (!FIsValidExpression(strExpr))
  958.                 return "Format(\"\", \"\", \"\")"
  959.  
  960.             //
  961.             // Replace all references to fields with GetValue statement
  962.             //
  963.  
  964.             strExpr = FixGetValue(objElem, strExpr);                
  965.  
  966.             //
  967.             // Fix up all instances of total functions (sum, count, avg, min, max)
  968.             //
  969.  
  970.             if (objElem.selectSingleNode("../../@idref") != null)
  971.                 {
  972.                 var objLevel = objElem.selectSingleNode("/RPTML/REPORT[0]/GROUP-LEVEL[@id='" + objElem.selectSingleNode("../../@idref").text + "']");
  973.                 var iLevelFilter = childNumber(objLevel) - 1;
  974.                 var strFilter = ", " + iLevelFilter;
  975.                 for (var iTotal = 0; iTotal < rgTotal.length; iTotal++)
  976.                     {
  977.                     strExpr = FixAggregate(rgTotal[iTotal], strExpr, strFilter);                    
  978.                     }
  979.                 }
  980.  
  981.             strExpr = FixFormat(strExpr);
  982.  
  983.             if (fUseFormat)
  984.                 strExpr = AddFormatting(strExpr, objFormat, objDecimalPlaces, objLcid);
  985.  
  986.             return strExpr;
  987.             }
  988.  
  989.         function FixFormat(strExpr)
  990.             {
  991.             var strFunctionName = "Format";
  992.             if (strExpr == null || strExpr.length == 0 || strExpr.match(new RegExp(strFunctionName + "\\(", "i")) == null)
  993.                 return strExpr;
  994.  
  995.             var strInFunction = "";
  996.             var strAfterFunction = "";
  997.             var strBeforeFunction = strExpr.replace(new RegExp("\^(.*)" + strFunctionName + ".*$", "i"), "$1" + strFunctionName + "\(");
  998.             var strTemp = strExpr.replace(new RegExp("\^.*" + strFunctionName + "\\(", "i"), "");
  999.             // find matching parens
  1000.             var len = strTemp.length
  1001.             var count = 1;
  1002.             for(var i=0; i<len; i++)
  1003.                 {
  1004.                 if (count > 0) //before the matching paren
  1005.                     {
  1006.                     if (strTemp.charAt(i) == '(')
  1007.                         count++;
  1008.                     else if (strTemp.charAt(i) == ')')
  1009.                         count--;
  1010.                     if (count > 0)
  1011.                         strInFunction += strTemp.charAt(i);
  1012.                     }
  1013.                 else //after the matching paren
  1014.                     {
  1015.                     strAfterFunction += strTemp.charAt(i);
  1016.                     }
  1017.                 }
  1018.  
  1019.             return strBeforeFunction + strInFunction + ", \"\")" + strAfterFunction;
  1020.             }
  1021.  
  1022.  
  1023.         function FixAggregate(strFunctionName, strExpr, strFilter)
  1024.             {
  1025.             if (strExpr == null || strExpr.length == 0 || strExpr.match(new RegExp(strFunctionName + "\\(", "i")) == null)
  1026.                 return strExpr;
  1027.  
  1028.             var strInFunction = "";
  1029.             var strAfterFunction = "";
  1030.             var strBeforeFunction = strExpr.replace(new RegExp("\^(.*)" + strFunctionName + ".*$", "i"), "$1" + strFunctionName + "\(");
  1031.             var strTemp = strExpr.replace(new RegExp("\^.*" + strFunctionName + "\\(", "i"), "");
  1032.             // find matching parens
  1033.             var len = strTemp.length
  1034.             var count = 1;
  1035.             for(var i=0; i<len; i++)
  1036.                 {
  1037.                 if (count > 0) //before the matching paren
  1038.                     {
  1039.                     if (strTemp.charAt(i) == '(')
  1040.                         count++;
  1041.                     else if (strTemp.charAt(i) == ')')
  1042.                         count--;
  1043.                     if (count > 0)
  1044.                         strInFunction += strTemp.charAt(i);
  1045.                     }
  1046.                 else //after the matching paren
  1047.                     {
  1048.                     strAfterFunction += strTemp.charAt(i);
  1049.                     }
  1050.                 }
  1051.  
  1052.             return strBeforeFunction + "\"" + DoubleQuotes(strInFunction) + "\"" + strFilter + ")" + strAfterFunction;
  1053.             }
  1054.  
  1055.         function FixGetValue(objElem, strExpr)
  1056.             {
  1057.             if (strExpr == null || strExpr.length == 0 || strExpr.match(/^[^\[]*\[[^\]]*\]/) == null)
  1058.                 return strExpr;
  1059.                         
  1060.             var strBeforeFunction = strExpr.replace(/^([^\[]*)\[.*$/, "$1");
  1061.             var strInFunction = FixupFieldNames(strExpr.replace(/^[^\[]*\[([^\]]*)\].*$/, "$1"), objElem, false);
  1062.             var strAfterFunction = strExpr.replace(/^[^\[]*\[[^\]]*\](.*)$/, "$1");
  1063.             var strDataType = GetNodeText(objElem, "/RPTML/REPORT[0]/DATA-MODEL/ROW-SOURCE/FIELD[@id = '" + strInFunction + "']/@datatype", "8");
  1064.             strInFunction = FixupFieldNames(strInFunction, objElem, true);
  1065.  
  1066.             return strBeforeFunction + "GetValue(\"" + strInFunction + "\", " + strDataType + ")" + FixGetValue(objElem, strAfterFunction);
  1067.             }
  1068.  
  1069.         function FixNzFunction(strExpr)
  1070.             {
  1071.             if (strExpr == null || strExpr.length == 0 || strExpr.match(/nz\(/gi) == null)
  1072.                 return strExpr;
  1073.  
  1074.             var strInFunction = "";
  1075.             var strAfterFunction = "";
  1076.             var strBeforeFunction = strExpr.replace(new RegExp("\^(.*)nz.*$", "i"), "$1nz\(");
  1077.             var strTemp = strExpr.replace(new RegExp("\^.*nz\\(", "i"), "");
  1078.             // find matching parens
  1079.             var len = strTemp.length
  1080.             var count = 1;
  1081.             for(var i=0; i<len; i++)
  1082.                 {
  1083.                 if (count > 0) //before the matching paren
  1084.                     {
  1085.                     if (strTemp.charAt(i) == '(')
  1086.                         count++;
  1087.                     else if (strTemp.charAt(i) == ')')
  1088.                         count--;
  1089.                     if (count > 0)
  1090.                         strInFunction += strTemp.charAt(i);
  1091.                     }
  1092.                 else //after the matching paren
  1093.                     {
  1094.                     strAfterFunction += strTemp.charAt(i);
  1095.                     }
  1096.                 }
  1097.  
  1098.             if (strInFunction.match(/,/) == null)
  1099.                 return strBeforeFunction + strInFunction + ",\"0\")" + strAfterFunction;
  1100.             else 
  1101.                 return strExpr;
  1102.             }
  1103.  
  1104.         function AddFormatting(strRef, objFormat, objDecimalPlaces, objLcid)
  1105.             {
  1106.             if (objFormat == null)
  1107.                 return "Format(" + strRef + ",\"\" ,\"\")";
  1108.  
  1109.             strFormat = DoubleQuotes(objFormat.text);
  1110.  
  1111.             switch (strFormat)
  1112.                 {
  1113.                 case "":
  1114.                 case "General Number":
  1115.                 case "General Date":
  1116.                 case "Long Date":
  1117.                 case "Long Time":
  1118.                 case "Medium Time":
  1119.                 case "Short Time":
  1120.                 case "True/False":
  1121.                 case "Yes/No":
  1122.                 case "On/Off":
  1123.                 case "Medium Date":
  1124.                 case "Short Date":
  1125.                     return "Format(" + strRef + ", \"" + strFormat + "\", \"\")";
  1126.                 case "Currency":
  1127.                     return "Format(" + strRef + ", \"" + strFormat + "\", \"" + GetNodeText(objLcid, ".", "") + "\")";
  1128.                 case "Euro":
  1129.                 case "Fixed":
  1130.                 case "Standard":
  1131.                 case "Percent":
  1132.                 case "Scientific":
  1133.                     return "Format(" + strRef + ", \"" + strFormat + "\", \"" + GetNodeText(objDecimalPlaces, ".", "2") + "\")";
  1134.                 }
  1135.  
  1136.             return "Format(" + strRef + ", \"" + strFormat + "\", \"\")";
  1137.             }
  1138.  
  1139.         function GetDecimals(objDecimalPlaces, nDefault)
  1140.             {
  1141.             var nDecimals = nDefault;
  1142.             if (objDecimalPlaces != null && objDecimalPlaces.text != 'auto' && Number(objDecimalPlaces.text) != nDefault)
  1143.                 nDecimals = objDecimalPlaces.text;
  1144.  
  1145.             if (nDecimals == 0)
  1146.                 return '';
  1147.  
  1148.             var strDecimals = '.';
  1149.             for (var i = 0; i < nDecimals; i++)
  1150.                 strDecimals = strDecimals + '0';
  1151.  
  1152.             return strDecimals;
  1153.             }
  1154.  
  1155.         function GetDataType(objFieldName)
  1156.             {
  1157.             if (objFieldName != null)
  1158.                 {
  1159.                 var strRecordSource = objFieldName.selectSingleNode("/RPTML/REPORT/RECORD-SOURCE").text;
  1160.                 strRecordSource = strRecordSource.replace(/^[ ]*(.+[^ ])[ ]*$/, '$1');        // remove spaces from beginning and end
  1161.                 var datatype = objFieldName.selectSingleNode("/RPTML/REPORT[0]/DATA-MODEL/ROW-SOURCE[@id='" + strRecordSource + "']/FIELD[@id='" + FixupFieldNames(objFieldName.text, objFieldName, false) + "']/@datatype");
  1162.                 if (datatype != null)
  1163.                     return datatype.text;
  1164.                 }
  1165.             return 8; // string type
  1166.             }
  1167.  
  1168.         function IsNumberType(nDataType, fIsDateANumber)
  1169.             {
  1170.             switch (nDataType)
  1171.                 {
  1172.                 case 16:    // adTinyInt
  1173.                 case 2:        // adSmallInt
  1174.                 case 3:        // adInteger
  1175.                 case 20:    // adBigInt
  1176.                 case 17:    // adUnsignedTinyInt
  1177.                 case 18:    // adUnsignedSmallInt
  1178.                 case 19:    // adUnsignedInt
  1179.                 case 21:    // adUnsignedBigInt
  1180.                 case 4:        // adSingle
  1181.                 case 5:        // adDouble
  1182.                 case 6:        // adCurrency
  1183.                 case 14:    // adDecimal
  1184.                 case 131:    // adNumeric
  1185.                 case 139:    // adVarNumeric
  1186.                 case 11:    // adBoolean
  1187.                     return true;
  1188.                 case 7:        // adDate
  1189.                 case 133:    // adDBDate
  1190.                 case 134:    // adDBTime
  1191.                 case 135:    // adDBTimeStamp
  1192.                     return fIsDateANumber;
  1193.                 case 8:        // adBSTR
  1194.                 case 120:    // adChar
  1195.                 case 200:    // adVarChar
  1196.                 case 201:    // adLongVarChar
  1197.                 case 130:    // adWChar:
  1198.                 case 202:    // adVarWChar
  1199.                 case 203:    // adLongVarWChar
  1200.                 default:
  1201.                     return false;
  1202.                 }
  1203.             }
  1204.  
  1205.         var rgTextAttr = new Array("COLOR", "BACKGROUND-COLOR", "BORDER-WIDTH", "BORDER-COLOR", "TEXT-ALIGN", "WRITING-MODE", "VISIBILITY",
  1206.                                    "FONT-WEIGHT", "FONT-SIZE", "FONT-FAMILY", "FONT-STYLE", "LEFT", "TOP", "WIDTH", "HEIGHT", "TEXT-DECORATION",
  1207.                                    "PADDING-TOP", "PADDING-BOTTOM", "PADDING-RIGHT", "PADDING-LEFT");
  1208.                                    
  1209.         function GetTextStyle(objElem, fIsSection)
  1210.             {
  1211.             var strStyle = GetBorderStyle(objElem);
  1212.             
  1213.             var objSource = objElem.selectSingleNode("CONTROL-SOURCE[not(@type)]");
  1214.             var objClass = objElem.selectSingleNode("CLASS");
  1215.             if (objSource != null && objClass != null)
  1216.                 {
  1217.                 var objTextAlign = objElem.selectSingleNode("/RPTML/REPORT[0]/STYLE[@id='" + objClass.text + "']/TEXT-ALIGN");
  1218.                 if (objTextAlign != null && objTextAlign.text == "general" && IsNumberType(Number(GetDataType(objSource)), true))
  1219.                     strStyle = strStyle + "TEXT-ALIGN: right; ";
  1220.                 }
  1221.  
  1222.             if (objElem.selectSingleNode("WIDTH") == null && fIsSection)
  1223.                 {
  1224.                 var strWidth = GetNodeText(objElem, "/RPTML/REPORT[0]/WIDTH", "");
  1225.                 if (strWidth != "")
  1226.                     strStyle = strStyle + "WIDTH: " + strWidth + "; ";
  1227.                 }
  1228.                 
  1229.             return strStyle + GetItemStyle(objElem, rgTextAttr);
  1230.             }
  1231.  
  1232.         function GetBorderStyle(objElem)
  1233.             {
  1234.             var objBS = objElem.selectSingleNode("BORDER-STYLE");
  1235.  
  1236.             if (objBS != null)
  1237.                 {
  1238.                 if (objBS.text == "solid" || objBS.text == "double-solid")
  1239.                     return "BORDER-STYLE: solid; ";
  1240.                 if (objBS.text == "dashed" || objBS.text == "short-dashes" || objBS.text == "dash-dot")
  1241.                     return "BORDER-STYLE: dashed; ";
  1242.                 if (objBS.text == "dotted" || objBS.text == "sparse-dots" || objBS.text == "dash-dot-dot")
  1243.                     return "BORDER-STYLE: dotted; ";
  1244.                 return "BORDER-STYLE: " + objBS.text + "; ";
  1245.                 }
  1246.             return "";
  1247.             }
  1248.  
  1249.         var rgCheckboxStyle = new Array("LEFT", "TOP", "HEIGHT", "VISIBILITY");
  1250.  
  1251.         function GetCheckboxStyle(objElem)
  1252.             {
  1253.             return GetItemStyle(objElem, rgCheckboxStyle);
  1254.             }
  1255.  
  1256.         var rgHyperlinkAttr = new Array("BORDER-WIDTH", "BORDER-COLOR", "BORDER-STYLE", "TEXT-ALIGN",
  1257.                                         "FONT-WEIGHT", "FONT-SIZE", "FONT-FAMILY", "FONT-STYLE", "VISIBILITY", "LEFT", "TOP", "WIDTH", "HEIGHT");
  1258.  
  1259.         function GetHyperlinkStyle(objElem)
  1260.             {
  1261.             return GetItemStyle(objElem, rgHyperlinkAttr);
  1262.             }
  1263.  
  1264.         var rgLineAttr = new Array("COLOR", "BACKGROUND-COLOR", "BORDER-WIDTH", "BORDER-COLOR", "BORDER-STYLE", "VISIBILITY",
  1265.                                    "LEFT", "TOP");
  1266.  
  1267.         function GetLineStyle(objElem)
  1268.             {
  1269.             var strStyle = GetItemStyle(objElem, rgLineAttr);
  1270.  
  1271.             var objBorder = GetStyleNode(objElem, "BORDER-WIDTH");
  1272.             if (objBorder != null)
  1273.                 {
  1274.                 var strWidth = objElem.selectSingleNode("WIDTH").text;
  1275.                 if (strWidth == "0in")
  1276.                     strWidth = objBorder.text;
  1277.  
  1278.                 var strHeight = objElem.selectSingleNode("HEIGHT").text;
  1279.                 if (strHeight == "0in")
  1280.                     strHeight = objBorder.text;
  1281.  
  1282.                 if (strStyle.length > 0)
  1283.                     strStyle = strStyle + "; ";
  1284.                 strStyle = strStyle + "WIDTH: " + strWidth;
  1285.                 strStyle = strStyle + "; HEIGHT: " + strHeight;
  1286.                 }
  1287.  
  1288.             return strStyle;
  1289.             }
  1290.  
  1291.         var rgRectAttr = new Array("COLOR", "BACKGROUND-COLOR", "BORDER-WIDTH", "BORDER-COLOR", "BORDER-STYLE", "VISIBILITY",
  1292.                                    "LEFT", "TOP", "WIDTH", "HEIGHT");
  1293.  
  1294.         function GetRectangleStyle(objElem)
  1295.             {
  1296.             return GetItemStyle(objElem, rgRectAttr);
  1297.             }
  1298.  
  1299.         var rgImageAttr = new Array("BORDER-WIDTH", "BORDER-COLOR", "BORDER-STYLE", "VISIBILITY",
  1300.                                     "LEFT", "TOP", "WIDTH", "HEIGHT");
  1301.  
  1302.         function GetImageStyle(objElem)
  1303.             {
  1304.             var strStyle = GetItemStyle(objElem, rgImageAttr);
  1305.  
  1306.             if (strStyle.length > 0)
  1307.                 strStyle = strStyle + "; ";
  1308.             strStyle = strStyle + "BACKGROUND-COLOR: silver";
  1309.  
  1310.             return strStyle;
  1311.             }
  1312.  
  1313.         var rgBodyAttr = new Array("BACKGROUND-POSITION", "BACKGROUND-REPEAT");
  1314.  
  1315.         function GetBodyStyle(objElem)
  1316.             {
  1317.             var strStyle = "";
  1318.             if (objElem != null)
  1319.                 {
  1320.                 strStyle = "BACKGROUND-IMAGE:url('" + objElem.text + "'); ";
  1321.                 strStyle += GetItemStyle(objElem.selectSingleNode("/RPTML/REPORT[0]"), rgBodyAttr);
  1322.                 }
  1323.             return strStyle;
  1324.             }
  1325.  
  1326.         function GetItemStyle(objElem, rgAttr)
  1327.             {
  1328.             var cStyles = 0;
  1329.             var strStyle = "";
  1330.  
  1331.             for (var iAttr in rgAttr)
  1332.                 if (objElem.selectSingleNode(rgAttr[iAttr]) != null)
  1333.                     {
  1334.                     if (cStyles > 0)
  1335.                         strStyle = strStyle + "; ";
  1336.                     strStyle = strStyle + rgAttr[iAttr] + ": " + objElem.selectSingleNode(rgAttr[iAttr]).text;
  1337.                     cStyles++;
  1338.                     }
  1339.  
  1340.             return strStyle;
  1341.             }
  1342.  
  1343.         function GetStyleNode(objElem, strName)
  1344.             {
  1345.             var objWidth = objElem.selectSingleNode(strName);
  1346.             if (objWidth == null)
  1347.                 objWidth = objElem.selectSingleNode("/RPTML/REPORT[0]/STYLE[@id='"+objElem.selectSingleNode("CLASS").text+"']/" + strName);
  1348.             return objWidth;
  1349.             }
  1350.  
  1351.         //
  1352.         // Gets the Field name from a table.field name, also makes sure field is correct case
  1353.         //
  1354.         function FixupFieldNames(strField, objElem, fConvertSpaces)
  1355.             {
  1356.             var strFieldName = strField.replace (/.*\./, ''); //strip out table in table.field
  1357.             var objFields = objElem.selectNodes("/RPTML/REPORT[0]/DATA-MODEL/ROW-SOURCE/FIELD[@id]");
  1358.             var strTemp = "";
  1359.  
  1360.             for (var i = 0; i < objFields.length; i++)
  1361.                 {
  1362.                 strTemp = GetNodeText(objFields.item(i), "@id", "");
  1363.                 if (strTemp.match(new RegExp("\^" + strFieldName + "$", "i")))
  1364.                     {
  1365.                     return (fConvertSpaces ? strTemp.replace(/ /g, '_x0020_') : strTemp); // spaces in data xml are _x0020_
  1366.                     }
  1367.                 else if (strTemp.match(new RegExp("\^" + strField + "$", "i")))
  1368.                     {
  1369.                     return (fConvertSpaces ? strTemp.replace(/ /g, '_x0020_') : strTemp); // spaces in data xml are _x0020_
  1370.                     }
  1371.                 }
  1372.             return (fConvertSpaces ? strFieldName.replace(/ /g, '_x0020_') : strFieldName); // spaces in data xml are _x0020_
  1373.             }    
  1374.  
  1375.         function DoubleQuotes(strExpr)
  1376.             {
  1377.             return strExpr.replace(/\"/g, "\"\"");
  1378.             }
  1379.  
  1380.         function GetCheckboxCondition(objElem)
  1381.             {
  1382.             var objSource = objElem.selectSingleNode("ENCODED-CONTROL-SOURCE");
  1383.             if (objSource != null)
  1384.                 {
  1385.                 if (GetNodeText(objSource, "@type", "") == "expression")
  1386.                     {
  1387.                     return FixExpression(objSource, objElem.selectSingleNode("FORMAT"), objElem.selectSingleNode("DECIMAL-PLACES"), objElem.selectSingleNode("FORMAT-LCID"), false);
  1388.                     }
  1389.                 else
  1390.                     {
  1391.                     strSource = FixupFieldNames(objSource.text, objElem, true);
  1392.                     var strCondition = 'GetValue("' + strSource + '", ' + GetDataType(objSource) + ')';
  1393.                     return strCondition;
  1394.                     }
  1395.                 }
  1396.             return '0';
  1397.             }
  1398.  
  1399.         function GetNodeText(objNode, strPath, strDefault)
  1400.             {
  1401.             var objResult;
  1402.             if (objNode != null)
  1403.                 objResult = objNode.selectSingleNode(strPath);
  1404.             return (objResult != null ? objResult.text : strDefault);
  1405.             }    
  1406.  
  1407.         // error messages defined
  1408.         var iError = -1;
  1409.         var rgErrorMessages = new Array("Your reportML has a problem and cannot be transformed.", // generic error message
  1410.                                         "This XSL does not support grouping on expressions at this time." // Grouped on Expressions
  1411.                                        );
  1412.  
  1413.         // this function checks the reportML properties to make sure we can present this report
  1414.         function FInvalidReportML(objElem)
  1415.             {
  1416.             // iError = 1 -> check for expressions for order-by clause
  1417.             var objNodes = objElem.selectNodes("/RPTML/REPORT[0]/GROUP-LEVEL");
  1418.             var objNode;
  1419.             for (var iNode = 0; iNode < objNodes.length; iNode++)
  1420.                 {
  1421.                 objNode = objNodes.item(iNode);
  1422.                 if (objNode.selectSingleNode("ENCODED-CONTROL-SOURCE[@type='expression']") != null)
  1423.                     {
  1424.                     iError = 1;
  1425.                     return true;
  1426.                     }
  1427.                 }
  1428.             return false;
  1429.             }
  1430.         
  1431.         function HandleErrors(objElem)
  1432.             {
  1433.             if ((iError >= 0) && iError < rgErrorMessages.length)
  1434.                 return "<HTML><BODY>" + rgErrorMessages[iError] + "</BODY></HTML>";
  1435.             return "<HTML><BODY>" + rgErrorMessages[0] + "</BODY></HTML>";
  1436.             }
  1437.  
  1438.         function FixInnerQuotes(strExpr)
  1439.             {
  1440.             var fNeedMatchSingleQuote = false;
  1441.             var fNeedMatchDoubleQuote = false;
  1442.             var fIsSingleQuote = false;
  1443.             var fIsDoubleQuote = false;
  1444.             var len = strExpr.length;
  1445.             var strTemp = "";
  1446.             for(var i=0; i<len; i++)
  1447.                 {
  1448.                 fIsDoubleQuote = strExpr.charAt(i) == '"';
  1449.                 fIsSingleQuote = strExpr.charAt(i) == "'";
  1450.                 if (fIsSingleQuote || fIsDoubleQuote)
  1451.                     {
  1452.                     if (fNeedMatchSingleQuote)
  1453.                         {
  1454.                         if (fIsDoubleQuote)
  1455.                             {
  1456.                             strTemp += "\"\"";
  1457.                             }
  1458.                         else
  1459.                             {
  1460.                             strTemp += "\"";
  1461.                             fNeedMatchSingleQuote = false;
  1462.                             }
  1463.                         }
  1464.                     else if (fNeedMatchDoubleQuote)
  1465.                         {
  1466.                         if (fIsDoubleQuote)
  1467.                             {
  1468.                             strTemp += "\"";
  1469.                             fNeedMatchDoubleQuote = false;
  1470.                             }
  1471.                         else
  1472.                             {
  1473.                             strTemp += "'";
  1474.                             }
  1475.                         }
  1476.                     else // found a beginning qoute
  1477.                         {
  1478.                         if (fIsDoubleQuote)
  1479.                             fNeedMatchDoubleQuote = true;
  1480.                         else
  1481.                             fNeedMatchSingleQuote = true;
  1482.                         strTemp += "\"";
  1483.                         }
  1484.                     }
  1485.                 else
  1486.                     {
  1487.                     strTemp += strExpr.charAt(i);
  1488.                     }
  1489.                 }
  1490.                 return strTemp;
  1491.             }
  1492.  
  1493.         function FIsValidExpression(strSource)
  1494.             {
  1495.             if ((strSource.match(/\[Forms\]\!/i) != null) || 
  1496.                 (strSource.match(/Forms\!/i) != null) ||
  1497.                 (strSource.match(/Forms\([^\!]*\!/i) != null) ||
  1498.                 (strSource.match(/\[Reports\]\!/i) != null)  || 
  1499.                 (strSource.match(/Reports\!/i) != null) ||
  1500.                 (strSource.match(/Reports\([^\!]*\!/i) != null) ||
  1501.                 (strSource.match(/\[Form\]\!/i) != null) || 
  1502.                 (strSource.match(/Form\!/i) != null) ||
  1503.                 (strSource.match(/Form\([^\!]*\!/i) != null) ||
  1504.                 (strSource.match(/\[Report\]\!/i) != null)  || 
  1505.                 (strSource.match(/Report\!/i) != null) ||
  1506.                 (strSource.match(/Report\([^\!]*\!/i) != null))
  1507.                 return false;
  1508.             else
  1509.                 return true;
  1510.             }
  1511.  
  1512.         function GetTableTags(objElem, fBeginTag)
  1513.             {
  1514.             if (GetNodeText(objElem, "/RPTML/REPORT[0]/LAYOUT", "") == "grid")
  1515.                 {
  1516.                 if (fBeginTag)
  1517.                     return "<TABLE BORDER=\"1\" BGCOLOR=\"#ffffff\" CELLSPACING=\"0\" CELLPADDING=\"0\"><TBODY>";
  1518.                 else
  1519.                     return "</TBODY></TABLE>";
  1520.                 }
  1521.             else
  1522.                 return "";
  1523.             }
  1524.  
  1525.     ]]></xsl:script>
  1526.  
  1527. </xsl:stylesheet>
  1528.  
  1529.